import ToDoList.Main;
import ToDoList.Task;

import org.apache.http.impl.client.HttpClientBuilder;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.RestTemplate;

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = Main.class
)
@ActiveProfiles("test")     // Load properties from application-test file
@DisplayName("Тест приложения")
public class ApplicationTest {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate testRestTemplate;
    private RestTemplate restTemplate;      // For patch requests

    private String rootLink;
    private String tasksLink;
    private final Task wrongTask = new Task();
    private final Task correctTask = new Task("title 1", "description 1");

    @BeforeEach
    public void init() {
        rootLink = "http://localhost:" + port;
        tasksLink = rootLink + "/tasks/";

        restTemplate = testRestTemplate.getRestTemplate();
        restTemplate.setRequestFactory(
                new HttpComponentsClientHttpRequestFactory(
                        HttpClientBuilder.create().build()      // Apache
                )
        );
    }

    @Test
    @DisplayName("Ошибка добавления задачи")
    public void test_01() {
        ResponseEntity<String> response = restTemplate.postForEntity(tasksLink, wrongTask, String.class);

        assertEquals(400, response.getStatusCodeValue());
    }

    @Test
    @DisplayName("Успешное добавление задачи")
    public void test_02() {
        for (int i = 1; i <= 2; i++) {
            ResponseEntity<String> response = restTemplate.postForEntity(tasksLink, correctTask, String.class);

            assertEquals(201, response.getStatusCodeValue());
            assertEquals("/tasks/" + i, response.getHeaders().getFirst("Location"));
        }
    }

    @Test
    @DisplayName("Неверный идентификатор задачи")
    public void test_03() {
        ResponseEntity<Task> response = restTemplate.getForEntity(tasksLink + "3", Task.class);

        assertEquals(404, response.getStatusCodeValue());
    }

    @Test
    @DisplayName("Успешное получение задачи")
    public void test_04() {
        for (int i = 1; i <= 2; i++) {
            ResponseEntity<Task> response = restTemplate.getForEntity(tasksLink + i, Task.class);

            assertEquals(200, response.getStatusCodeValue());
            assertEquals((Integer) i, response.getBody().getId());
            assertEquals(correctTask.getCreationTime(), response.getBody().getCreationTime());
            assertEquals(correctTask.getIsDone(), response.getBody().getIsDone());
            assertEquals(correctTask.getTitle(), response.getBody().getTitle());
            assertEquals(correctTask.getDescription(), response.getBody().getDescription());
        }
    }

    @Test
    @DisplayName("Получение списка задач")
    public void test_05() {
        ResponseEntity<Task[]> response = restTemplate.getForEntity(tasksLink, Task[].class);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals(2, response.getBody().length);

        for (int i = 1; i <= 2; i++) {
            assertEquals((Integer) i, response.getBody()[i - 1].getId());
            assertEquals(correctTask.getCreationTime(), response.getBody()[i - 1].getCreationTime());
            assertEquals(correctTask.getIsDone(), response.getBody()[i - 1].getIsDone());
            assertEquals(correctTask.getTitle(), response.getBody()[i - 1].getTitle());
            assertEquals(correctTask.getDescription(), response.getBody()[i - 1].getDescription());
        }
    }

    @Test
    @DisplayName("Изменение isDone")
    public void test_06() {
        wrongTask.setIsDone(true);
        String body = restTemplate.patchForObject(tasksLink + "1", wrongTask, String.class);

        ResponseEntity<Task> response = restTemplate.getForEntity(tasksLink + "1", Task.class);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals((Integer) 1, response.getBody().getId());
        assertEquals(wrongTask.getIsDone(), response.getBody().getIsDone());
    }

    @Test
    @DisplayName("Изменение title")
    public void test_07() {
        wrongTask.setTitle("title 2");
        String body = restTemplate.patchForObject(tasksLink + "1", wrongTask, String.class);

        ResponseEntity<Task> response = restTemplate.getForEntity(tasksLink + "1", Task.class);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals((Integer) 1, response.getBody().getId());
        assertEquals(wrongTask.getTitle(), response.getBody().getTitle());
    }

    @Test
    @DisplayName("Изменение description")
    public void test_08() {
        wrongTask.setDescription("description 2");
        String body = restTemplate.patchForObject(tasksLink + "1", wrongTask, String.class);

        ResponseEntity<Task> response = restTemplate.getForEntity(tasksLink + "1", Task.class);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals((Integer) 1, response.getBody().getId());
        assertEquals(wrongTask.getDescription(), response.getBody().getDescription());
    }

    @Test
    @DisplayName("Повтор изменений")
    public void test_09() {
        String body = restTemplate.patchForObject(tasksLink + "1", wrongTask, String.class);

        assertEquals("No data to change", body);
    }

    @Test
    @DisplayName("Удаление задачи")
    public void test_10() {
        for (int i = 1; i <= 2; i++) {
            restTemplate.delete(tasksLink + i);

            ResponseEntity<Task> response = restTemplate.getForEntity(tasksLink + i, Task.class);

            assertEquals(404, response.getStatusCodeValue());
        }
    }

    @Test
    @DisplayName("Дата и время")
    public void test_90() {
        ResponseEntity<String> response = restTemplate.getForEntity(rootLink, String.class);

        assertTrue(response.getBody().matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d+ \\d+"));
    }
}
